#include <dalelib/graphics/shader.h>
#include <iostream>

using namespace std;

namespace dalelib
{

void cgErrorCallback();

class ShaderController
{
public:

	CGcontext _context;
	CGprofile _vert_profile;
	CGprofile _frag_profile;
	bool _setup;

	ShaderController()
		: _setup( false )
	{ }

	~ShaderController()
	{
//		cgDestroyContext(_context);
//		if( _context ) delete _context;
//		if( _vert_profile ) delete _vert_profile;
//		if( _frag_profile ) delete _frag_profile;
	}

	void setup()
	{
		_setup = true;

		_context = cgCreateContext();

//		_vert_profile = cgGLGetLatestProfile( CG_GL_VERTEX );
		_vert_profile = CG_PROFILE_ARBVP1;
		cgGLSetOptimalOptions( _vert_profile );

//		_frag_profile = cgGLGetLatestProfile( CG_GL_FRAGMENT );
		_frag_profile = CG_PROFILE_ARBFP1;
		cgGLSetOptimalOptions( _frag_profile );

		cgSetErrorCallback(cgErrorCallback);

	}

};

ShaderController g_shader_controller;

void cgErrorCallback()
{
	CGerror err = cgGetError();
	if(err)
	{
		cout << cgGetErrorString(err) << endl;
		if(g_shader_controller._context != NULL) cout << cgGetLastListing(g_shader_controller._context) << endl;
//		exit(0);
	}
}


void Shader::load(string _ShaderName)
{
	loadVertProgram(_ShaderName);
	loadFragProgram(_ShaderName);
}

void Shader::loadVertProgram(string _Name)
{
	if( !g_shader_controller._setup ) g_shader_controller.setup();

	_vert_program = cgCreateProgramFromFile( g_shader_controller._context, CG_SOURCE, _Name.c_str(), g_shader_controller._vert_profile, "vertMain", 0 );

	if( _vert_program )
	{
		cgGLLoadProgram( _vert_program );
	}
	else
	{
		CGerror Error = cgGetError();
//		MessageBox(NULL, cgGetErrorString(Error), (string("Vertex program error: ")+_Name).c_str(), MB_OK);
	}
}

void Shader::loadFragProgram(string _Name)
{
	if( !g_shader_controller._setup ) g_shader_controller.setup();

	_frag_program = cgCreateProgramFromFile( g_shader_controller._context, CG_SOURCE, _Name.c_str(), g_shader_controller._frag_profile, "fragMain", 0 );

	if( _frag_program )
	{
		cgGLLoadProgram( _frag_program );
	}
	else
	{
		CGerror Error = cgGetError();
//		MessageBox(NULL, cgGetErrorString(Error), (string("Fragment program error: ")+_Name).c_str(), MB_OK);
	}

}

void Shader::addVertParam(std::string _Name)
{
	_params[_Name] = cgGetNamedParameter(_vert_program, _Name.c_str());
}

void Shader::addFragParam(std::string _Name)
{
	_params[_Name] = cgGetNamedParameter(_frag_program, _Name.c_str());
}

void Shader::bind()
{
	if(_vert_program)
	{
		cgGLEnableProfile(g_shader_controller._vert_profile);
		cgGLBindProgram(_vert_program);
	}
	if(_frag_program)
	{
		cgGLEnableProfile(g_shader_controller._frag_profile);
		cgGLBindProgram(_frag_program);
	}
}

void Shader::unbind()
{
	if(_vert_program)
	{
		cgGLDisableProfile(g_shader_controller._vert_profile);
	}
	if(_frag_program)
	{
		cgGLDisableProfile(g_shader_controller._frag_profile);
	}
}

void Shader::setParam(std::string _Param, float _Value0) { cgSetParameter1f( _params[_Param], _Value0 ); }
void Shader::setParam(std::string _Param, int _Value0) { cgSetParameter1i( _params[_Param], _Value0 ); }
void Shader::setParam(std::string _Param, float _Value0, float _Value1) { cgSetParameter2f( _params[_Param], _Value0, _Value1 ); }
void Shader::setParam(std::string _Param, v2 _Value) { cgSetParameter2f( _params[_Param], _Value.x(), _Value.y() ); }
void Shader::setParam(std::string _Param, float _Value0, float _Value1, float _Value2) { cgSetParameter3f( _params[_Param], _Value0, _Value1, _Value2 ); }
void Shader::setParam(std::string _Param, v3 _Value) { cgSetParameter3f( _params[_Param], _Value.x(), _Value.y(), _Value.z() ); }
void Shader::setParam(std::string _Param, float _Value0, float _Value1, float _Value2, float _Value3) { cgSetParameter4f( _params[_Param], _Value0, _Value1, _Value2, _Value3 ); }
void Shader::setParam(std::string _Param, v4 _Value) { cgSetParameter4f( _params[_Param], _Value.x(), _Value.y(), _Value.z(), _Value.w() ); }
void Shader::setParamArray1(std::string _Param, const int _Num, float* _Data) { cgGLSetParameterArray1f( _params[_Param], 0, _Num, _Data ); }
void Shader::setParamArray2(std::string _Param, const int _Num, float* _Data) { cgGLSetParameterArray2f( _params[_Param], 0, _Num, _Data ); }
void Shader::setParamArray3(std::string _Param, const int _Num, float* _Data) { cgGLSetParameterArray3f( _params[_Param], 0, _Num, _Data ); }
void Shader::setParamArray4(std::string _Param, const int _Num, float* _Data) { cgGLSetParameterArray4f( _params[_Param], 0, _Num, _Data ); }
void Shader::setParamMatrix(std::string _Param, CGGLenum _Value0, CGGLenum _Value1) { cgGLSetStateMatrixParameter( _params[_Param], _Value0, _Value1 ); }
void Shader::setParamMatrix(std::string _Param, m4 _Matrix) { cgSetMatrixParameterfc( _params[_Param], (float*)_Matrix.ptr() ); }
void Shader::setParamTex(std::string _Param, GLuint _Texture) { cgGLSetTextureParameter( _params[_Param], _Texture ); cgGLEnableTextureParameter( _params[_Param] ); }
//void Shader::setParamTex2D(std::string _Param, GLuint _Texture) { cgGLSetTextureParameter( _params[_Param], _Texture ); cgGLEnableTextureParameter( _params[_Param] ); }
//void Shader::setParamTexCube(std::string _Param, GLuint _Texture) { cgGLSetTextureParameter( _params[_Param], _Texture ); cgGLEnableTextureParameter( _params[_Param] ); }
bool Shader::paramExists(string _Param) { return _params.find(_Param)!=_params.end(); }

} // namespace dalelib
